import os
import pandas as pd
from datetime import timedelta
from typing import Annotated, Optional, List
from pydantic import BaseModel, Field
from sqlalchemy import func
from sqlmodel import select
from fastapi import Depends, APIRouter, Query, status, File, UploadFile, HTTPException
from dependencies.database import SessionDep
from models.auth import UserDB, UserPublic, authenticate_user, create_access_token, get_current_active_user, get_user
from utils.config import settings
from utils.response_utils import response_value
from utils.sysutils import verify_password, get_password_hash
from utils.validate_utils import validate_email

from fastapi.responses import StreamingResponse
from io import BytesIO
from uuid import uuid4

import logging

logger = logging.getLogger(__name__)

# 创建一个 APIRouter 实例，用于定义路由
router = APIRouter()


# 定义 LoginRequest 模型，用于接收登录请求的数据
class LoginRequest(BaseModel):
    username: str
    password: str


# 登录
@router.post("/login", response_model=dict, summary="用户登录", description="使用用户名和密码登录并返回访问令牌")
async def login(
        form_data: LoginRequest,
        db: SessionDep
) -> dict:
    """
    处理用户登录请求，验证用户名和密码，并返回访问令牌。

    :param form_data: 包含用户名和密码的表单数据
    :param db: 数据库会话对象
    :return: 包含 code、data 和 msg 的字典
    """

    user = authenticate_user(db, form_data.username, form_data.password)
    if not user:
        return response_value(
            code=status.HTTP_401_UNAUTHORIZED,
            msg="用户名或密码不正确"
        )
    access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        data={"sub": user.username}, expires_delta=access_token_expires
    )
    return response_value(data={"access_token": access_token, "name": user.username})


"""
    登出logout
    当路由处理函数被调用时，FastAPI 会执行以下步骤：
        解析 current_user 依赖，调用 get_current_active_user 函数。
        get_current_active_user 调用 get_current_user 获取当前用户信息。
        根据 get_current_user 的返回值，get_current_active_user 返回 UserDB 或 JSONResponse。
        最终，current_user 被注入到路由处理函数中。
"""


@router.post("/logout", summary="用户登出", description="用户登出")
async def logout(
        # Depends 表示 current_user 的值由 get_current_active_user 函数提供。
        current_user: Annotated[UserDB | dict, Depends(get_current_active_user)],
        db: SessionDep
) -> dict:
    """
    处理用户登出请求，并返回成功消息。
    :param current_user: 当前用户对象
    :param db: 数据库会话对象
    :return: 包含 code、data 和 msg 的字典
    """
    if isinstance(current_user, dict):
        return current_user  # 直接返回错误响应

    # 正常业务逻辑
    return response_value()


# 定义 ChangePasswordRequest 模型，用于接收修改密码请求的数据
class ChangePasswordRequest(BaseModel):
    old_password: str = Field(..., alias="oldPassword")  # 支持 oldPassword 和 old_password
    new_password: str = Field(..., alias="newPassword")  # 支持 newPassword 和 new_password


# 定义 /change-password 路由，用于修改密码
@router.post("/user/change-password", summary="修改密码", description="修改当前用户的密码")
async def change_password(
        request: ChangePasswordRequest,
        current_user: Annotated[UserDB | dict, Depends(get_current_active_user)],
        db: SessionDep
) -> dict:
    """
    修改当前用户的密码。

    :param request: 包含旧密码和新密码的请求体
    :param current_user: 当前用户对象
    :param db: 数据库会话对象
    :return: 成功消息
    :raises HTTPException: 如果用户不存在或旧密码不正确
    """
    if isinstance(current_user, dict):
        return current_user  # 直接返回错误响应

    user = get_user(db, current_user.username)
    if not user:
        return response_value(code=404, msg="无此用户")

    if not verify_password(request.old_password, user.password):
        return response_value(code=400, msg="旧密码不正确")

    new_password = get_password_hash(request.new_password)
    user.password = new_password
    db.add(user)
    db.commit()
    db.refresh(user)

    return response_value(data={"message": "密码更新成功"})


@router.post("/user/rest_password", summary="重置密码", description="重置当前用户的密码")
async def rest_password(
        # 前端请求参数{"id": 2}
        request: dict,
        current_user: Annotated[UserDB | dict, Depends(get_current_active_user)],
        db: SessionDep
) -> dict:
    """
    修改当前用户的密码。
    :param current_user: 当前用户对象
    :param db: 数据库会话对象
    :return: 成功消息
    :raises HTTPException: 如果用户不存在或旧密码不正确
    """
    if isinstance(current_user, dict):
        return current_user  # 直接返回错误响应

    user = db.get(UserDB, request.get('id'))
    if not user:
        return response_value(code=404, msg="无此用户")
    new_password = get_password_hash("123456")
    user.password = new_password
    try:
        db.commit()
        db.refresh(user)
        logger.info("密码重置成功")
    except Exception as e:
        db.rollback()
        logger.error(f"密码重置失败: {str(e)}", exc_info=True)
        return response_value(code=400, msg="密码重置失败")

    return response_value()


@router.get(
    "/user/list",
    response_model=dict,
    summary="获取所有用户",
    description="获取所有用户的列表，支持分页"
)
def read_users(
        current_user: Annotated[UserDB | dict, Depends(get_current_active_user)],
        db: SessionDep,
        pageNum: int = 1,  # 统一命名风格
        pageSize: Annotated[int, Query(le=100, description="分页限制，默认为 100，最大为 100")] = 100,
        username: Optional[str] = None,  # 可选：根据用户账号筛选
        gender: Optional[int] = None,  # 可选：根据性别筛选
        minAge: Optional[int] = None,  # 可选：最小年龄
        maxAge: Optional[int] = None,  # 可选：最大年龄
        idCard: Optional[str] = None,  # 统一命名风格
        email: Optional[str] = None,  # 可选：根据邮箱筛选
        status: Optional[int] = None,  # 可选：根据状态筛选
        startTime: Optional[str] = None,  # 可选：开始时间
        endTime: Optional[str] = None,  # 可选：结束时间
) -> dict:
    """
    获取所有用户的列表，支持分页和筛选。

    :param current_user: 当前用户对象
    :param db: 数据库会话对象
    :param pageNum: 当前页码，默认为 1
    :param pageSize: 分页限制，默认为 100，最大为 100
    :param username: 筛选条件：用户账号
    :param gender: 筛选条件：性别
    :param minAge: 筛选条件：最小年龄
    :param maxAge: 筛选条件：最大年龄
    :param idCard: 筛选条件：身份证号
    :param email: 筛选条件：邮箱
    :param status: 筛选条件：状态
    :param startTime: 筛选条件：开始时间
    :param endTime: 筛选条件：结束时间
    :return: 包含 code、data 和 msg 的字典
    """
    if isinstance(current_user, dict):
        return current_user  # 直接返回错误响应

    # 计算 offset
    offset = (pageNum - 1) * pageSize

    # 构建查询条件
    query = select(UserDB)
    if username:
        # query = query.where(UserDB.username.ilike(f"%{username}%"))
        # query = query.where(func.ilike(UserDB.username, f"%{username}%"))
        # query = query.where(func.lower(UserDB.username).like(func.lower(f"%{username}%")))
        query = query.where(func.lower(UserDB.username).ilike(f"%{username}%"))
    if gender is not None:
        query = query.where(UserDB.gender == gender)
    if minAge is not None:
        query = query.where(UserDB.age >= minAge)
    if maxAge is not None:
        query = query.where(UserDB.age <= maxAge)
    if idCard:
        query = query.where(UserDB.idCard == idCard)
    if email:
        query = query.where(UserDB.email == email)
    if status is not None:
        query = query.where(UserDB.status == status)
    if startTime:
        query = query.where(UserDB.createTime >= startTime)
    if endTime:
        query = query.where(UserDB.createTime <= endTime)

    # 执行查询
    users = db.exec(query.offset(offset).limit(pageSize)).all()

    # 获取总数
    total = db.exec(select(func.count()).select_from(query.subquery())).first()

    # 转换为 UserPublic 模型
    user_list = []
    for user in users:
        user_dict = UserPublic.model_validate(user).model_dump()
        user_dict["gender_str"] = user.gender_str
        user_dict["status_str"] = user.status_str
        user_list.append(user_dict)

    # 返回符合前端格式的响应
    return response_value(data={
        "list": user_list,
        "page_num": pageNum,
        "page_size": pageSize,
        "total": total
    })


@router.post("/user/export", summary="导出用户数据为 Excel", description="导出所有用户的列表为 Excel 文件")
async def export_users(
        current_user: Annotated[UserDB | dict, Depends(get_current_active_user)],
        db: SessionDep,
        username: Optional[str] = None,  # 可选：根据用户名筛选
        gender: Optional[int] = None,  # 可选：根据性别筛选
        age: Optional[int] = None,  # 可选：根据年龄筛选
        id_card: Optional[str] = None,  # 可选：根据身份证号筛选
        email: Optional[str] = None,  # 可选：根据邮箱筛选
        status: Optional[int] = None,  # 可选：根据状态筛选
):
    """
    导出所有用户的列表为 Excel 文件。

    :param current_user: 当前用户对象
    :param db: 数据库会话对象
    :param username: 筛选条件：用户名
    :param gender: 筛选条件：性别
    :param age: 筛选条件：年龄
    :param id_card: 筛选条件：身份证号
    :param email: 筛选条件：邮箱
    :param status: 筛选条件：状态
    :return: Excel 文件的 StreamingResponse
    """
    if isinstance(current_user, dict):
        return current_user  # 直接返回错误响应

    # 构建查询条件
    query = select(UserDB)
    if username:
        query = query.where(UserDB.username == username)
    if gender is not None:
        query = query.where(UserDB.gender == gender)
    if age is not None:
        query = query.where(UserDB.age == age)
    if id_card:
        query = query.where(UserDB.idCard == id_card)
    if email:
        query = query.where(UserDB.email == email)
    if status is not None:
        query = query.where(UserDB.status == status)

    # 执行查询
    users = db.exec(query).all()

    # 转换为 UserPublic 模型
    user_list = [UserPublic.model_validate(user) for user in users]

    # 将 UserPublic 对象转换为字典列表，并替换 gender 和 status 为 gender_str 和 status_str
    user_dicts = []
    for user in user_list:
        user_dict = {
            "ID": user.id,
            "用户名": user.username,
            "年龄": user.age,
            "性别": user.gender_str,
            "身份证号": user.idCard,
            "邮箱地址": user.email,
            "地址": user.address,
            "详细信息": user.detail,
            "创建时间": user.createTime,
            "状态": user.status_str,
            "头像URL": user.avatar
        }
        user_dicts.append(user_dict)

    # 创建 DataFrame
    df = pd.DataFrame(user_dicts)

    # 将 DataFrame 写入 Excel 文件
    output = BytesIO()
    with pd.ExcelWriter(output, engine='openpyxl') as writer:
        df.to_excel(writer, index=False, sheet_name='Users')

    # 设置输出流的位置到文件开头
    output.seek(0)

    # 返回 StreamingResponse
    headers = {
        "Content-Disposition": "attachment; filename=users.xlsx"
    }
    return StreamingResponse(output, media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                             headers=headers)


@router.post("/user/import", summary="批量导入用户", description="根据上传的 Excel 文件批量导入用户")
async def import_users(
        current_user: Annotated[UserDB | dict, Depends(get_current_active_user)],
        db: SessionDep,
        file: UploadFile = File(...)
) -> dict:
    """
    批量导入用户。

    :param current_user: 当前用户对象
    :param file: 上传的 Excel 文件
    :param db: 数据库会话对象
    :return: 包含 code、data 和 msg 的字典
    """
    if isinstance(current_user, dict):
        return current_user  # 直接返回错误响应

    try:
        # 读取上传的 Excel 文件
        contents = await file.read()
        df = pd.read_excel(BytesIO(contents))

        # 验证 Excel 文件中的列
        required_columns = {"ID", "用户名", "年龄", "性别", "身份证号", "邮箱地址", "地址", "详细信息", "创建时间",
                            "状态", "头像URL"}
        missing_columns = required_columns - set(df.columns)
        if missing_columns:
            return response_value(code=400, msg=f"Excel 文件格式不正确，缺少必要的列：{', '.join(missing_columns)}")

        # 性别和状态的映射
        gender_map = {"男": 1, "女": 2}
        status_map = {"启用": 1, "禁用": 0}

        created_users = []
        errors = []

        for index, row in df.iterrows():
            try:
                # 处理每个字段的空值并设置默认值
                username = row["用户名"] if pd.notnull(row["用户名"]) else ""
                age = row["年龄"] if pd.notnull(row["年龄"]) else None
                gender_str = row["性别"] if pd.notnull(row["性别"]) else "男"
                id_card = row["身份证号"] if pd.notnull(row["身份证号"]) else ""
                email = row["邮箱地址"] if pd.notnull(row["邮箱地址"]) else ""
                address = row["地址"] if pd.notnull(row["地址"]) else ""
                detail = row["详细信息"] if pd.notnull(row["详细信息"]) else ""
                create_time = row["创建时间"] if pd.notnull(row["创建时间"]) else pd.Timestamp.now()
                status_str = row["状态"] if pd.notnull(row["状态"]) else "启用"
                avatar = row["头像URL"] if pd.notnull(row["头像URL"]) else ""

                # 映射性别和状态
                gender = gender_map.get(gender_str, 1)
                status_value = status_map.get(status_str, 1)

                # 验证 username 是否为空
                if not username:
                    raise HTTPException(status_code=400, detail=f"第 {index + 1} 行：用户名不能为空")

                # 验证邮箱格式
                if email and not validate_email(email):
                    raise HTTPException(status_code=400, detail=f"第 {index + 1} 行：邮件格式无效")

                # 检查用户名是否已存在
                db_user = get_user(db, username)
                if db_user:
                    raise HTTPException(status_code=400, detail=f"第 {index + 1} 行：用户名已注册")

                # 验证 age 是否为有效的整数
                if age is not None:
                    try:
                        age = int(age)
                        if age < 0 or age > 150:  # 合理的年龄范围限制
                            raise ValueError("年龄超出合理范围")
                    except (ValueError, TypeError):
                        raise HTTPException(status_code=400, detail=f"第 {index + 1} 行：年龄格式无效或超出合理范围")
                else:
                    age = None

                # 创建新用户
                user_data = {
                    "username": username,
                    "age": age,
                    "gender": gender,
                    "idCard": id_card,
                    "email": email,
                    "address": address,
                    "detail": detail,
                    "createTime": create_time,
                    "status": status_value,
                    "avatar": avatar,
                    "photo": [{"url": avatar}] if avatar else [],  # 处理照片字段
                    "password": get_password_hash("123456")  # 直接哈希密码
                }

                new_user = UserDB(**user_data)
                db.add(new_user)
                created_users.append(new_user)

            except HTTPException as http_exc:
                errors.append(http_exc.detail)

        # 如果有错误，回滚事务并返回错误信息
        if errors:
            db.rollback()
            return response_value(code=400, msg="\n".join(errors))

        # 提交事务
        db.commit()

        return response_value(data={"created_users": [user.id for user in created_users]}, msg="用户批量导入成功")

    except Exception as e:
        # 回滚事务
        db.rollback()
        logger.error(f"批量导入用户时发生错误: {str(e)}", exc_info=True)
        return response_value(code=500, msg=f"批量导入用户时发生错误: {str(e)}")


@router.post("/user/add", response_model=dict, summary="创建用户", description="创建新用户并处理照片数据")
async def create_user(current_user: Annotated[UserDB | dict, Depends(get_current_active_user)], user: UserDB,
                      db: SessionDep):
    """ 创建一个新的用户。
    :param current_user: 当前用户对象
    :param user: 包含用户信息的请求体
    :param db: 数据库会话对象
    :return: 新创建的用户信息
    :raises HTTPException: 如果用户名已存在或验证失败
    """
    if isinstance(current_user, dict):
        return current_user  # 直接返回错误响应

    # 验证 username 是否为空
    if not user.username:
        return response_value(code=400, msg="用户名不能为空")

    # 验证邮箱格式
    if not validate_email(user.email):
        return response_value(code=400, msg="邮件格式无效")

    # 检查用户名是否已存在
    db_user = get_user(db, user.username)
    if db_user:
        return response_value(code=400, msg="用户名已注册")

    # 处理照片数据（保留必要字段）
    processed_photos = [
        {"url": photo["url"], "name": photo.get("name")}
        for photo in user.photo
        if "url" in photo
    ]

    # 构建用户对象
    new_user = UserDB(
        **user.model_dump(exclude={"photo", "password"}),
        photo=processed_photos,  # 直接传递处理后的字典列表
        password="123456"
    )

    # 将新用户添加到数据库会话
    db.add(new_user)

    try:
        # 提交事务
        db.commit()
        # refresh 方法会从数据库中获取对象的最新数据，并将其同步到内存中的对象实例中。
        db.refresh(new_user)
    except Exception as e:
        # 如果有错误，回滚事务并返回错误信息
        db.rollback()
        logger.error(f"创建用户时发生错误: {str(e)}", exc_info=True)
        error_messages = [str(err) for err in e.args] if e.args else [str(e)]
        return response_value(code=400, msg="创建用户时发生错误：\n".join(error_messages))

    return response_value(
        data={"user": UserPublic.model_validate(new_user).model_dump()},
        msg="用户创建成功"
    )


@router.get("/user/{user_id}", response_model=UserPublic, summary="获取单个用户", description="根据 ID 获取单个用户")
async def read_user(
        current_user: Annotated[UserDB | dict, Depends(get_current_active_user)],
        user_id: int,
        db: SessionDep
):
    """
    根据 ID 获取单个用户。

    :param current_user: 当前用户对象
    :param user_id: 用户 ID
    :param db: 数据库会话对象
    :return: 用户信息
    :raises HTTPException: 如果用户不存在
    """
    if isinstance(current_user, dict):
        return current_user  # 直接返回错误响应

    user = db.get(UserDB, user_id)
    if not user:
        return response_value(code=400, msg="无此用户")
    user_data = user.model_dump(exclude_unset=True)
    if 'photo' in user_data:
        user_data['photo'] = [
            {"url": p["url"], "name": p.get("name")}
            for p in user_data['photo']
            if "url" in p
        ]
    return user


@router.post("/user/edit", response_model=dict, summary="修改用户", description="修改用户数据")
async def update_user(current_user: Annotated[UserDB | dict, Depends(get_current_active_user)], user: UserDB,
                      db: SessionDep):
    """ 更新
    :param current_user: 当前用户对象
    :param user: 包含更新信息的请求体
    :param db: 数据库会话对象
    :return: 更新后的用户信息
    """

    if isinstance(current_user, dict):
        return current_user  # 直接返回错误响应

    db_user = db.get(UserDB, user.id)
    if not db_user:
        return response_value(code=400, msg="无此用户")

    # 验证邮箱格式
    if 'email' in user.model_dump() and not validate_email(user.email):
        return response_value(code=400, msg="邮件格式无效")

    # 处理照片数据（保留必要字段）
    processed_photos = [
        {"url": photo["url"], "name": photo.get("name")} for photo in user.photo if "url" in photo
    ]
    user.photo = processed_photos

    # 排除 密码和创建时间
    user_dict = user.model_dump(exclude={"password", "createTime"})

    # 更新数据库中的用户信息
    for key, value in user_dict.items():
        setattr(db_user, key, value)

    try:
        # 提交事务
        db.commit()
        # refresh 方法会从数据库中获取对象的最新数据，并将其同步到内存中的对象实例中。
        db.refresh(db_user)
    except Exception as e:
        # 如果有错误，回滚事务并返回错误信息
        db.rollback()
        logger.error(f"更新时发生错误: {str(e)}", exc_info=True)
        error_messages = [str(err) for err in e.args] if e.args else [str(e)]
        return response_value(code=400, msg="更新时发生错误：\n".join(error_messages))

    return response_value(
        data={"user": UserPublic.model_validate(db_user).model_dump()},
        msg="更新成功"
    )


class DeleteUserRequest(BaseModel):
    id: List[int]


@router.post("/user/delete", summary="删除用户", description="根据 ID 批量删除用户")
async def delete_user(
        current_user: Annotated[UserDB | dict, Depends(get_current_active_user)],
        request: DeleteUserRequest,
        db: SessionDep
):
    """
    根据 ID 批量删除用户。

    :param current_user: 当前用户对象
    :param request: 包含要删除的用户ID列表的请求体
    :param db: 数据库会话对象
    :return: 成功消息
    :raises HTTPException: 如果用户不存在
    """
    if isinstance(current_user, dict):
        return current_user  # 直接返回错误响应

    user_ids = request.id
    if not user_ids:
        return response_value(code=400, msg="用户ID列表为空")

    try:
        # 查询要删除的用户
        users_to_delete = db.query(UserDB).filter(UserDB.id.in_(user_ids)).all()

        if not users_to_delete:
            return response_value(code=400, msg="无此用户")

        # 删除用户
        for user in users_to_delete:
            db.delete(user)

        # 提交事务
        db.commit()

        return response_value(data={"deleted_ids": [user.id for user in users_to_delete]}, msg="用户删除成功")

    except Exception as e:
        # 回滚事务
        db.rollback()
        logger.error(f"批量删除用户时发生错误: {str(e)}", exc_info=True)
        return response_value(code=500, msg=f"批量删除用户时发生错误: {str(e)}")


# 定义文件上传目录（静态文件目录的子目录）
UPLOAD_DIR = "static/uploads"

# 确保上传目录存在
os.makedirs(UPLOAD_DIR, exist_ok=True)


# 访问路径 http://localhost:6061/static/uploads/

# 单文件和多文件上传接口
@router.post("/file/upload/img", summary="上传图片（单文件或多文件）")
async def upload_files(
        current_user: Annotated[UserDB, Depends(get_current_active_user)],
        files: Optional[List[UploadFile]] = File(None),
        file: Optional[UploadFile] = File(None)
):
    if isinstance(current_user, dict):
        return current_user  # 直接返回错误响应
    try:
        if files:
            # 多文件上传
            file_urls = []
            for file_item in files:
                # print("1", file_item.filename)
                # 生成唯一文件名
                file_extension = os.path.splitext(file_item.filename)[1]
                unique_filename = f"{uuid4()}{file_extension}"
                file_path = os.path.join(UPLOAD_DIR, unique_filename)

                # 保存文件
                with open(file_path, "wb") as buffer:
                    content = await file_item.read()
                    buffer.write(content)

                # 构造访问URL（确保与静态文件服务路径匹配）
                base_url = settings.BASE_URL.rstrip("/")
                file_url = f"{base_url}/static/uploads/{unique_filename}"
                file_urls.append(file_url)

            return response_value(data={"fileUrls": file_urls}, msg="图片上传成功")


        elif file:
            # print("2", file.filename)
            # 单文件上传
            # 生成唯一文件名
            file_extension = os.path.splitext(file.filename)[1]
            unique_filename = f"{uuid4()}{file_extension}"
            file_path = os.path.join(UPLOAD_DIR, unique_filename)

            # 保存文件
            with open(file_path, "wb") as buffer:
                content = await file.read()
                buffer.write(content)

            # 构造访问URL（确保与静态文件服务路径匹配）
            base_url = settings.BASE_URL.rstrip("/")  # base_url http://127.0.0.1:6061
            file_url = f"{base_url}/static/uploads/{unique_filename}"

            return response_value(data={"fileUrl": file_url}, msg="图片上传成功")

        else:
            return response_value(code=400, msg="没有文件上传")

    except Exception as e:
        logger.error(f"上传文件失败: {str(e)}", exc_info=True)
        return response_value(code=500, msg=f"文件上传失败: {str(e)}")


# 视频上传接口
@router.post("/file/upload/video", summary="上传单个视频")
async def upload_single_video(
        current_user: Annotated[UserDB, Depends(get_current_active_user)],
        file: UploadFile = File(...)
):
    if isinstance(current_user, dict):
        return current_user  # 直接返回错误响应
    try:
        # 生成唯一文件名
        file_extension = os.path.splitext(file.filename)[1]
        unique_filename = f"{uuid4()}{file_extension}"
        file_path = os.path.join(UPLOAD_DIR, unique_filename)

        # 保存文件
        with open(file_path, "wb") as buffer:
            content = await file.read()
            buffer.write(content)

        # 构造访问URL（确保与静态文件服务路径匹配）
        base_url = settings.BASE_URL.rstrip("/")
        file_url = f"{base_url}/static/uploads/{unique_filename}"
        return response_value(data={"fileUrl": file_url}, msg="视频上传成功")

    except Exception as e:
        logger.error(f"上传视频失败: {str(e)}", exc_info=True)
        return response_value(code=500, msg=f"视频上传失败: {str(e)}")
